#ifndef RENDERER_H
#define RENDERER_H
#include <QGLWidget>
#include "common.h"
#include <QGLShader>
#include <QGLShaderProgram>
#include <string>
#include <QGLFramebufferObject>
#include <stdio.h>
#include <list>
#include "barkgen.h"

#include "GL/glu.h"
#define PLANE_SIZE 1000
#define TILING_FACTOR 10
#define TOTAL_TILES TILING_FACTOR * TILING_FACTOR
#define TILE_LENGTH PLANE_SIZE/TILING_FACTOR
#define RANDOM_GRASS 1000
//#define LIMIT_VIEW
#define ENVIRONMENT_VIEW
#define GRASSW 30
#define GRASSH 10
#define VERT_LIMIT_B 20
#define VERT_LIMIT_T 65
#define LOOK_LIMIT 0.0
#define DENSITY_CONSTANT 80000

class Scene3D;

class Camera;
class QFile;
//Main Rendering Class


class Renderer : public QGLWidget {
    Q_OBJECT
signals:
     void _mouseDrag(QMouseEvent *);
public:
    Renderer(QWidget *parent = 0);
    ~Renderer();
    QHash<QString, QGLShaderProgram *> shader_programs;
       QHash<QString, GLuint> framebuffer_objects;
    std::vector<float3> grass_positions;
    bool modeInteract;
    //Texture info
    GLuint mSkybox[6];
     GLuint mCubeID;
     GLuint soil_id;
    //Shader Linking and binding
    bool linkShader(QString shader) { return shader_programs[shader]->link(); }
    bool bindShader(QString shader) {return shader_programs[shader]->bind(); }
     void removeShaders(QString shader) { shader_programs[shader]->removeAllShaders();}
     void releaseShader(std::string shader){
         QString str = QString::fromStdString(shader);
         shader_programs[str]->release();
     }


    //Sets and loads shaders
     void setCurrentShader(std::string shader);
     void loadShaders();

     //For rendering to a quad
     void textured_quad(int w, int h, bool flip);
     void orthoMode(int w, int h);
    void renderScene(bool depth_mode);

     float lambda;
     float tropism;
     float aux_angle;
     float shed_thres;
     float term_weight;
     float aux_weight;
     float diameter_rate;
     float tlength;
     float alength;
     float alpha;
     void applyValues();
     void iterate();
     void clear();
     void updateBark();
     Scene3D *m_scene;
    Camera *mCamera;

    bool saveScene(QString filename);
    bool loadScene(QString filename);
    GLuint bark_common;
    GLuint bark_lenticel;
    GLuint bark_split;
    bool grass_on;
    bool bark_mode;
    //TESTING PURPOSES
    BarkGen testBark;
    BarkStruct testBarkStruct;
protected:
    void renderGrass(float2 position);
     //Methods
     void updateCamera();
     //Inherited Methods
     void initializeGL();
     void paintGL();
    // Interaction events
     void resizeGL(int w, int h);
     void mousePressEvent(QMouseEvent *event);
     void mouseMoveEvent(QMouseEvent *event);
     void keyPressEvent(QKeyEvent *event);
     void keyReleaseEvent(QKeyEvent *event);
     void wheelEvent(QWheelEvent *event);
          void textured_quad_crop(int w, int h, bool flip);
     void mouseReleaseEvent(QMouseEvent *event);
     void updateInteraction();
    void pickTree();
     //Scene initialization and rendering methjods
     void renderSkybox(GLuint mSkybox[6]) ;

     //Framebuffer methods
     void load_framebuffers(int w, int h);
     //Texturing methods
      GLuint loadTexture(const QString &filename);
      GLuint loadCubeMap(QList<QFile *> files);
      //texture ID's
      GLuint palm_texture;
      GLuint grass_strip_id;
      GLuint grass_id;
      GLuint grass_distorted_id;
      //framebuffer IDs
      GLuint depth_id;
      GLuint color_id;
      GLuint renderBuffer;



  private:
      void realloc_framebuffers(int w, int h);
      //Rotation and interaction variables
     float2 mLastMousePos;
     //Interaction booleans
     bool left;
     bool right;
     bool up;
     bool down;
     bool pan_up;
     bool pan_down;
     bool plant;
     bool forward, backward;
     QTimer *mTimer;
     float mFPS, mIncrement;
     float time;
     bool left_click;
     float3 click_up_pos;


     GLUquadric *mQuadric;
     float3 patchLocations[TOTAL_TILES];
     float2 random_grass_pos[RANDOM_GRASS];
     //Test
     float3 curIntersect;

protected slots:
     void mouseDragEvent(QMouseEvent *event);
     void redraw();
};

#endif // RENDERER_H
